package com.utils;


import com.crypto.BitcoinUtils;
import org.apache.commons.io.FileUtils;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.interfaces.ECPrivateKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.PKCS8Generator;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JcaPKCS8Generator;
import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8EncryptorBuilder;
import org.bouncycastle.operator.InputDecryptorProvider;
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
import sun.security.ec.ECPublicKeyImpl;

import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.security.*;

/**
 * 该类以后主要放key相关的工具
 * @author zyf
 */
public class KeyUtil {

    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * decrypt pem and convert it to privateKey
     * 从私钥pem文件获得私钥
     * @param pemFilePath   PEM文件路径
     * @param pass          PRI密码
     * @return
     */
    @Deprecated
    public static PrivateKey readPemPrivateKey(String pemFilePath, String pass) throws Exception {
        String priPem = FileUtils.readFileToString(new File(pemFilePath),  Charset.forName("UTF-8"));
        PrivateKey privateKey = generatePrikeyWithPem(priPem, pass);
        return privateKey;
    }


    /**
     * 暂时不支持设置SecureRandom与iterationCount
     * convert privateKey to pem and encrypt
     * @param privateKey  私钥
     * @param pass        密码
     * @param algorithm   加密算法
     * @return
     * @throws Exception
     */
    public static String generatePrikeyPemWithPass (PrivateKey privateKey, String pass, ASN1ObjectIdentifier algorithm, AlgorithmIdentifier prf) throws Exception {
        JceOpenSSLPKCS8EncryptorBuilder encryptorBuilder = new JceOpenSSLPKCS8EncryptorBuilder(algorithm);
        encryptorBuilder.setProvider("BC");
        encryptorBuilder.setPasssword(pass.toCharArray());
        encryptorBuilder.setPRF(prf);
        PKCS8Generator pkcs8 = new JcaPKCS8Generator(privateKey, encryptorBuilder.build());
        return PemUtil.toPemString(pkcs8);
    }

    /**
     * decrypt pemString and convert it to privateKey
     * @param pemPathOrString   priPem是pem文件路径或读取pem私钥文件得到的字符串
     * @param pass              密码
     * @return PrivateKey
     * @throws Exception
     */
    public static PrivateKey generatePrikeyWithPem (String pemPathOrString, String pass) throws Exception {
        String priPemString = pemPathOrString;
        File pemFile = new File(pemPathOrString);
        if (pemFile.isFile()) {
            priPemString = FileUtils.readFileToString(pemFile,  Charset.forName("UTF-8"));
        }
        PEMParser pemParser = new PEMParser(new StringReader(priPemString));
        Object object = pemParser.readObject();
        PrivateKey privateKey;
        if (pass.trim() == "" || pass == null) {
            if (object instanceof PEMKeyPair) {
                PEMKeyPair kp = (PEMKeyPair)object;
                privateKey = new JcaPEMKeyConverter().setProvider("BC").getPrivateKey(kp.getPrivateKeyInfo());
            } else if (object instanceof PrivateKeyInfo) {
                privateKey = new JcaPEMKeyConverter().setProvider("BC").getPrivateKey((PrivateKeyInfo)object);
            } else {
                throw new IOException("unrecognised private key pemFile or pemString");
            }
//            try {
//                privateKey = new JcaPEMKeyConverter().setProvider("BC").getPrivateKey((PrivateKeyInfo)object);
//            } catch (ClassCastException ccEx) {
//                privateKey = new JcaPEMKeyConverter().setProvider("BC").getPrivateKey(((PEMKeyPair)object).getPrivateKeyInfo());
//            }
        } else {
            InputDecryptorProvider decProv = new JceOpenSSLPKCS8DecryptorProviderBuilder().setProvider("BC").build(pass.toCharArray());
            PKCS8EncryptedPrivateKeyInfo pInfo = (PKCS8EncryptedPrivateKeyInfo)object;
            privateKey = new JcaPEMKeyConverter().setProvider("BC").getPrivateKey(pInfo.decryptPrivateKeyInfo(decProv));
        }
        return privateKey;
    }

    /**
     * 根据给定的私钥返回公钥
     * {@link #getPubFromPrivateKey(PrivateKey) "均利用了BC，二者实现略微不同"}
     * @param bcecPrivateKey
     * @return
     * @throws Exception
     */
    public static PublicKey getPubFromPrivateKey(BCECPrivateKey bcecPrivateKey) throws Exception{
        ECParameterSpec ecSpec = bcecPrivateKey.getParameters();
        ECPoint Q = ecSpec.getG().multiply(bcecPrivateKey.getD());
        byte[] publicDerBytes = Q.getEncoded(false);
        ECPoint point = ecSpec.getCurve().decodePoint(publicDerBytes);
        ECPublicKeySpec pubSpec = new ECPublicKeySpec(point, ecSpec);
        KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC");
        return keyFactory.generatePublic(pubSpec);
    }

    /**
     * 根据给定的私钥返回公钥
     * {@link #getPubFromPrivateKey(BCECPrivateKey) "均利用了BC，二者实现略微不同"}
     * @param privateKey
     * @return
     * @throws Exception
     */
    public static PublicKey getPubFromPrivateKey(PrivateKey privateKey) throws Exception {
        ECPrivateKey ecPrivateKey = (ECPrivateKey) privateKey;
        ECParameterSpec ecSpec = ecPrivateKey.getParameters();
        ECPoint Q = ecSpec.getG().multiply(ecPrivateKey.getD());
        ECPublicKeySpec pubSpec = new ECPublicKeySpec(Q, ecSpec);
        KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC");
        return keyFactory.generatePublic(pubSpec);
    }

    /**
     * 通过privateKey生成PEMKeyPair，进而可以获得publicKey
     * @param privateKey    PrivateKey实例
     * @return
     * @throws Exception
     */
    public static PEMKeyPair generateKPbyPrivateKey(PrivateKey privateKey) throws Exception{
        String priPemString = PemUtil.toPemString(privateKey);  // pkcs1
        PEMParser pemParser = new PEMParser(new StringReader(priPemString));
        PEMKeyPair pemKeyPair = (PEMKeyPair)pemParser.readObject();
        return pemKeyPair;
    }

    /**
     * 根据公钥获取短地址
     * @param publicKey
     * @return
     * @throws Exception
     */
    public static String getAddrByPublicKey(PublicKey publicKey) throws Exception{
        String certAddr;
        if (publicKey instanceof BCECPublicKey) {
            certAddr = BitcoinUtils.calculateBitcoinAddress(((BCECPublicKey)publicKey).getQ().getEncoded(false));
        } else if (publicKey instanceof ECPublicKeyImpl){
            certAddr = BitcoinUtils.calculateBitcoinAddress(((ECPublicKeyImpl)publicKey).getEncodedPublicValue());
        } else {
            throw new InvalidKeyException("识别不了的PublicKey");
        }
        return certAddr;
    }

}
